<?php

namespace App\Common\SDK\Domain;

use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Support\Facades\Log;

/**
 * Class Base
 * @package App\Common\SDK\Domain
 * @property Client $httpClient
 */
abstract class Base
{
    const METHOD_GET = 'GET';
    const METHOD_POST = 'POST';
    const METHOD_PUT = 'PUT';
    const METHOD_DELETE = 'DELETE';

    const FORMATTED_DATA_TYPE_JSON = 'json';
    const FORMATTED_DATA_TYPE_FORM_DATA = 'multipart';
    const FORMATTED_DATA_TYPE_URLENCODE = 'form_params';

    protected $httpClient;

    public function __construct()
    {

    }

    /**
     * @return mixed
     */
    public function getHttpClient()
    {
        return $this->httpClient;
    }

    /**
     * @param $client
     * @return $this
     */
    public function setHttpClient($client)
    {
        $this->httpClient = $client;

        return $this;
    }

    protected function assembleParams($data)
    {
        return $data;
    }

    /**
     * 请求结束后的操作
     * @param $response
     * @return mixed
     */
    protected function requestAfter($response)
    {
        return $response;
    }

    /**
     * @param string $method
     * @param string $uri
     * @param array $data
     * @param string $formattedDataType
     * @return mixed
     * @throws \Exception
     */
    public function request(string $method, string $uri, $data = [], $formattedDataType = self::FORMATTED_DATA_TYPE_FORM_DATA)
    {
        try {
            $st = microtime(true);

            $data = $this->assembleParams($data);
            $response = $this->httpClient->request(
                $method,
                $uri,
                $this->dealWithFormattedData($method, $data, $formattedDataType)
            );

            $duration = round((microtime(true) - $st) * 1000, 2);
            $content = $response->getBody()->getContents() ?? '[]';

            $sdkInfo = sprintf('[SDK] 请求接口: %s [%s] 参数: %s 返回: %s',
                $this->httpClient->getConfig('base_uri') . $uri,
                $method,
                json_encode($data),
                $content);
            Log::{empty($content) ? 'warning' : 'info'}($sdkInfo, [
                'duration' => $duration,
            ]);

            return $this->requestAfter($content);
        } catch (GuzzleException $e) {
            Log::error(sprintf('[SDK] 请求接口: %s [%s] 参数: %s 发生错误: %s',
                $this->httpClient->getConfig('base_uri') . $uri,
                $method,
                json_encode($data),
                $e->getMessage()));

            throw new \Exception($e->getMessage(), $e->getCode());
        }
    }

    /**
     * 封装处理 Data 数据
     * @param        $method
     * @param $data
     * @param string $formattedDataType
     * @return array
     */
    protected function dealWithFormattedData($method, $data, $formattedDataType = self::FORMATTED_DATA_TYPE_FORM_DATA)
    {
        if (isset($data['headers'])){
            $headers = $data['headers'];
            unset($data['headers']);
        }

        switch ($method) {
            case self::METHOD_GET:
            case self::METHOD_DELETE:
                $formattedData = [
                    'query' => $data,
                ];
                break;
            case self::METHOD_POST:
            case self::METHOD_PUT:
                $formattedData = [
                    'form_params' => $data,
                ];

                if ($formattedDataType === self::FORMATTED_DATA_TYPE_FORM_DATA) {
                    $formattedData = [
                        'multipart' => $this->generateFormData($data),
                    ];
                } elseif ($formattedDataType === self::FORMATTED_DATA_TYPE_JSON) {
                    $formattedData = [
                        'json' => $data,
                    ];
                }
                break;
            default:
                $formattedData = $data;
                break;
        }
        if (isset($headers)){
            $formattedData['headers'] = $headers;
        }
        return $formattedData;
    }

    /**
     * 处理成 Form Data
     * @param $data
     * @return array
     */
    private function generateFormData($data)
    {
        $result = [];
        foreach ($data as $k => $v) {
            $tmp = [
                'name' => $k,
                'contents' => \is_array($v) ? json_encode($v) : $v,
            ];
            if (\is_resource($v)) {
                $meta_data = stream_get_meta_data($v);
                $tmp['filename'] = basename($meta_data['uri']);
            }
            $result[] = $tmp;
        }

        return $result;
    }
}
